package com.lcn.core.service.aop;

import com.lcn.comm.domain.TxRequest;
import com.lcn.comm.domain.TxTransactionInfo;
import com.lcn.comm.domain.TxTransactionLocal;
import com.lcn.comm.framework.task.TaskGroupManager;
import com.lcn.comm.framework.task.TxTask;
import com.lcn.comm.service.aop.TransactionService;
import com.lcn.comm.service.db.LCNDataSource;
import com.lcn.comm.service.transaction.TransactionGroupService;
import com.lcn.comm.util.DateUtil;
import com.weibo.api.motan.config.springsupport.BasicServiceConfigBean;
import com.weibo.api.motan.config.springsupport.annotation.MotanReferer;
import com.weibo.api.motan.util.LoggerUtil;
import org.apache.commons.lang3.StringUtils;
import org.aspectj.lang.ProceedingJoinPoint;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.concurrent.TimeUnit;

/**
 * <p>类说明</p>
 *
 * @author 张峰 zfvip_it@163.com
 * @create: 2017-12-25 16:22
 */
@Service("runningTransactionService")
public class RunningTransactionServerImpl implements TransactionService {

    @Resource
    private LCNDataSource lcnDataSource;

    @MotanReferer
    private TransactionGroupService transactionGroupService;

    @Resource
    private BasicServiceConfigBean basicServiceConfigBean;

    /**
     * 事务逻辑处理
     *
     * @param point
     * @param info
     * @return
     * @throws Throwable
     */
    public Object execute(ProceedingJoinPoint point, TxTransactionInfo info) throws Throwable {
        Long startTime = System.currentTimeMillis();
        TxRequest txRequest = info.getTxRequest();
        //判断是否同一事务组下
        boolean isHasGroup = lcnDataSource.hasGroup(txRequest.getGroupId());
        //将事务组信息缓存到当前线程
        TxTransactionLocal txTransactionLocal = cacheTxTransactionLocal(txRequest, isHasGroup);
        try {
            Object res = point.proceed();
            if (!txTransactionLocal.isReadOnly()) {//写操作处理
                //添加事务组
                addTransactionGroup(isHasGroup, txRequest);
            }
            return res;
        } catch (Throwable e) {
            throw e;
        } finally {
            TxTransactionLocal.setCurrent(null);
            LoggerUtil.info("事务运行结束，groupId[{}],requestId[{}],耗时[{}]", txRequest.getGroupId(), txRequest.getRequestId(), DateUtil.dateDiff(startTime, System.currentTimeMillis()));
        }
    }

    /**
     * 添加事务组
     *
     * @param isHasGroup
     * @param txRequest
     */
    private void addTransactionGroup(boolean isHasGroup, TxRequest txRequest) throws Exception {
        //添加事务组
        boolean result = transactionGroupService.addTransactionGroup(isHasGroup, txRequest);
        if (!isHasGroup) {
            TxTask txTask = TaskGroupManager.getInstance().getTask(txRequest.getGroupId(), txRequest.getRequestId());
            while (txTask != null && !txTask.isAwait()) {
                TimeUnit.MILLISECONDS.sleep(1);
            }
            //事务组添加失败
            if (!result) {
                //通知业务回滚
                if (txTask != null) {
                    //修改事务组状态
                    txTask.setState(0);
                    txTask.signalTask();
                    throw new Exception("添加事务组失败,groupId" + txRequest.getGroupId());
                }
            }
        }
    }

    /**
     * 将事务组信息缓存到当前线程
     *
     * @param txRequest
     * @param isHasGroup
     */
    private TxTransactionLocal cacheTxTransactionLocal(TxRequest txRequest, boolean isHasGroup) {
        if (StringUtils.isNotEmpty(txRequest.getGroupId())) {
            TxTransactionLocal txTransactionLocal = new TxTransactionLocal();
            //事务组ID
            txTransactionLocal.setGroupId(txRequest.getGroupId());
            //请求id
            txTransactionLocal.setRequestId(txRequest.getRequestId());
            //是否发起发模块
            txTransactionLocal.setHasStart(false);
            //是否同一模块节点
            txTransactionLocal.setHasGroup(isHasGroup);
            //请求超时时间
            txTransactionLocal.setRequestTimeout(basicServiceConfigBean.getRequestTimeout());
            TxTransactionLocal.setCurrent(txTransactionLocal);
            return txTransactionLocal;
        }
        return null;
    }
}
